#ifndef VL_BUFFER__H_
#define VL_BUFFER__H_

#include <cstdint>
#include <memory>
#include <string>
#include <cstring>

namespace VL {

/**
 * @Author: CimZzz
 * @Date: 2022-01-07 14:19:36
 * 
 * 缺省初始化缓冲区长度
 */
#define BUFFER_DEFAULT_INIT_SIZE 255

/**
 * @Author: CimZzz
 * @Date: 2022-01-07 14:39:25
 * 
 * 最小字节增长尺寸
 */
#define BUFFER_MIN_INC_SIZE 64

/**
 * @Author: CimZzz
 * @Date: 2022-07-19 18:49:34
 * 
 * 字节缓冲区异常
 */
class VLBufferException {
public:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 18:50:20
	 * 
	 * 构造方法
	 * @param msg 异常消息
	 */
	explicit VLBufferException(std::string msg);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 18:51:01
	 * 
	 * 获取异常消息
	 */
	std::string getMessage() const;

private:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 18:50:12
	 * 
	 * 异常消息
	 */
	std::string message;
};

/**
 * @Author: CimZzz
 * @Date: 2022-08-01 19:11:47
 * 
 * 缓存范围区
 */
struct BufferRange {
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-01 19:12:23
	 * 
	 * 标识当前范围区是否合法
	 */
	bool isValid;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-01 19:12:47
	 * 
	 * 字节缓冲区起始下标
	 */
	uint32_t begin;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-01 19:12:58
	 * 
	 * 字节缓存区终止下标
	 */
	uint32_t end;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-01 19:30:49
	 * 
	 * 需要跳过的尺寸
	 */
	uint32_t skipCount;
};

/**
 * @Author: CimZzz
 * @Date: 2022-01-07 14:30:26
 * 
 * 字节缓冲区
 */
class Buffer {
public:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-01 19:18:04
	 * 
	 * 缓冲区匹配模式
	 */
	enum BufferMatchMode {
		/**
		 * @Author: CimZzz
		 * @Date: 2022-08-03 14:20:31
		 * 
		 * 无匹配模式
		 */
		BMC_None = 0x0,

		/**
		 * @Author: CimZzz
		 * @Date: 2022-08-01 19:17:08
		 * 
		 * 返回的匹配范围中, 包含待匹配子串
		 */
		BMC_Include_Match = 0x1,

		/**
		 * @Author: CimZzz
		 * @Date: 2022-08-01 19:17:45
		 * 
		 * 如果遍历到缓冲区尾部未能匹配子串, 则认定为匹配成功, 范围是从读取下标至缓冲区末尾
		 */
		BMC_Include_EOF = 0x4
	};

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 14:14:06
	 * 
	 * 构造函数，初始化缓冲长度
	 * @param initSize 初始化缓冲长度
	 */
	explicit Buffer(uint32_t initSize = BUFFER_DEFAULT_INIT_SIZE);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 14:21:04
	 * 
	 * 析构函数
	 * 回收缓冲资源
	 */
	~Buffer();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 11:39:07
	 * 
	 * 获取可读取缓存长度
	 * @return 可读取缓存长度
	 */
	uint32_t getReadableLength() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 17:02:46
	 * 
	 * 获取当前缓冲长度
	 * @return 缓冲长度
	 * 
	 * @note 已读取的字节并不会使缓存长度发生变化, 只有调用 [dropRead] 之后才会将缓存内容移除掉
	 */
	uint32_t getBufferLength() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-10 16:57:05
	 * 
	 * 获取当前缓冲区已申请的内存长度
	 * @return 已申请的内存长度
	 */
	uint32_t getTotalLength() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-10 17:06:07
	 * 
	 * 重置当前内存缓冲区
	 */
	void reset();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 14:29:07
	 * 
	 * 复制字节到当前缓冲区中
	 * @param buffer 字节数组指针
	 * @param offset 偏移位置
	 * @param length 复制长度
	 */
	void appendBuffer(const uint8_t* buffer, uint32_t offset, uint32_t length);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 14:29:07
	 * 
	 * 复制字节到当前缓冲区中
	 * @param buffer 字节缓冲区
	 * @param offset 偏移位置
	 * @param length 复制长度，0 表示复制到末尾
	 */
	void appendBuffer(const Buffer* buffer, uint32_t offset = 0, uint32_t length = 0);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-02-15 18:05:52
	 * 
	 * 复制字符串到当前缓冲区中
	 * @param str 字符串
	 */
	void appendStrBuffer(const char * str);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 18:44:31
	 * 
	 * 复制字符串到当前缓冲区中
	 * @param str 字符串
	 */
	void appendStrBuffer(const std::string& str);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 15:58:10
	 * 
	 * 复制单字节到当前缓冲区中
	 * @param byte 单字节
	 */
	void appendByte(uint8_t byte);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-14 15:24:27
	 * 
	 * 在当前缓冲区中插入字节
	 * @param insertIndex 插入位置
	 * @param buffer 字节数组指针
	 * @param offset 偏移位置
	 * @param length 复制长度
	 * 
	 * @note 如果插入位置超过当前字节长度，那么缓存字节长度会变更到插入之后的字节尾部，中间间隔部分会填充 0
	 */
	void insertBuffer(uint32_t insertIndex, const uint8_t* buffer, uint32_t offset, uint32_t length);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-14 15:25:13
	 * 
	 * 在当前缓冲区中插入字节
	 * @param insertIndex 插入位置
	 * @param buffer 字节缓冲区
	 * @param offset 偏移位置
	 * @param length 复制长度，0 表示复制到末尾
	 * 
	 * @note 如果插入位置超过当前字节长度，那么缓存字节长度会变更到插入之后的字节尾部，中间间隔部分会填充 0
	 */
	void insertBuffer(uint32_t insertIndex, const Buffer* buffer, uint32_t offset = 0, uint32_t length = 0);	
	
	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 15:58:10
	 * 
	 * 在当前缓冲区中插入单字节
	 * @param insertIndex 插入位置
	 * @param byte 单字节
	 * 
	 * @note 如果插入位置超过当前字节长度，那么缓存字节长度会变更到插入之后的字节尾部，中间间隔部分会填充 0
	 */
	void appendByte(uint32_t insertIndex, uint8_t byte);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-12 17:05:13
	 * 
	 * 将打印的字符串添加到当前缓冲区中
	 * @param fmt 格式化字符串
	 * @param ... 变长参数，用于填充格式化字符串参数
	 */
	void appendPrint(const char * fmt, ...);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-12 17:05:13
	 * 
	 * 将打印的字符串添加到当前缓冲区中
	 * @param fmt 格式化字符串
	 * @param va 变长参数，用于填充格式化字符串参数
	 */
	void appendVPrint(const char * fmt, va_list va);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-11 18:54:06
	 * 
	 * 从字节缓冲区中移除字节
	 * @param offset 偏移位置
	 * @param length 移除长度
	 * 
	 * @note 当 offset == 0, length == 0 时，作用等同于 [reset]
	 */
	void removeRange(uint32_t offset = 0, uint32_t length = 0);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-08 12:08:34
	 * 
	 * 从字节缓冲区尾部移除字节
	 * 
	 * @param length 移除长度
	 */
	void removeLast(uint32_t length = 0);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 14:22:31
	 * 
	 * 克隆当前缓冲资源
	 * @param offset 偏移位置
	 * @param length 克隆长度，0 表示克隆到末尾
	 */
	uint8_t* getBufferClone(uint32_t offset = 0, uint32_t length = 0);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 17:04:45
	 * 
	 * 直接获取当前缓存区指针
	 * @return 缓冲区指针
	 */
	uint8_t* getBuffer() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-03 16:02:41
	 * 
	 * 直接将当前缓存区作为字符指针返回
	 * 
	 * @return 缓冲区指针
	 * 
	 * @note 自行裁定当前缓冲区是否可以当做字符串指针返回
	 */
	const char* getBufferAsChar() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-10 16:56:37
	 * 
	 * 确保当前缓冲区已申请内存长度
	 * @param size 已申请内存长度
	 */
	bool makeSureTotalLength(uint32_t size);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-02-15 17:18:36
	 * 
	 * 读取一个单字节
	 * 
	 * @return 单字节
	 * 
	 * @attention 读取失败时会抛出 [VLBufferException] 异常
	 */
	int8_t readByte();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 18:48:24
	 * 
	 * 读取整数
	 * 
	 * @param byteLength 字节长度, 默认整数为 4 字节, 可以按需指定字节长度
	 * @param bigEndian 是否为大端字节序, 默认为是
	 * 
	 * @return 整数
	 * 
	 * @attention 读取失败时会抛出 [VLBufferException] 异常
	 */
	long long readInt(unsigned char byteLength = 4, bool bigEndian = true);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 18:56:31
	 * 
	 * 读取浮点数
	 * 
	 * @param byteLength 字节长度, 默认整数为 4 字节, 可以按需指定字节长度
	 * @param bigEndian 是否为大端字节序, 默认为是
	 * 
	 * @return 浮点数
	 * 
	 * @attention 读取失败时会抛出 [VLBufferException] 异常
	 */
	float readFloat(bool bigEndian = true);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 18:56:31
	 * 
	 * 读取双精度浮点数
	 * 
	 * @param byteLength 字节长度, 默认整数为 4 字节, 可以按需指定字节长度
	 * @param bigEndian 是否为大端字节序, 默认为是
	 * 
	 * @return 双精度浮点数
	 * 
	 * @attention 读取失败时会抛出 [VLBufferException] 异常
	 */
	double readDouble(bool bigEndian = true);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 11:42:43
	 * 
	 * 读取字符串
	 * 
	 * @param strLength 字符串长度
	 * 
	 * @return 字符串
	 * 
	 * @attention 读取失败时会抛出 [VLBufferException] 异常
	 */
	std::shared_ptr<char> readChars(unsigned int strLength);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 18:58:42
	 * 
	 * 读取字符串
	 * 
	 * @param strLength 字符串长度
	 * 
	 * @return 字符串
	 * 
	 * @attention 读取失败时会抛出 [VLBufferException] 异常
	 */
	std::string readString(unsigned int strLength);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 11:47:02
	 * 
	 * 匹配字符串, 直到发现相同串时返回
	 * 
	 * @param matchStr 待匹配子串
	 * @param mode 匹配模式
	 * 
	 * @return 匹配后的字符串, 如果匹配失败, 则返回 nullptr
	 * 
	 * @note 使用 KMP 算法进行字符串匹配
	 * @note 无论是否返回包含子串, 读取下标都会越过子串
	 */
	std::shared_ptr<char> matchString(const char * matchStr, uint32_t mode = BufferMatchMode::BMC_None);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-01 17:43:14
	 * 
	 * 匹配字符串, 直到发现相同串时返回
	 * 如果相同串是 notMatchStr 的子串, 则继续寻找
	 * 
	 * @param matchStr 待匹配子串
	 * @param notMatchStr 不匹配子串
	 * @param mode 匹配模式
	 * 
	 * @return 匹配后的字符串, 如果匹配失败, 则返回 nullptr
	 * 
	 * @note 使用 KMP 算法进行字符串匹配
	 * @note 无论是否返回包含子串, 读取下标都会越过子串
 	 * @note 不匹配子串长度必须大于匹配子串
	 */
	std::shared_ptr<char> matchString(const char * matchStr, const char * notMatchStr, uint32_t mode = BufferMatchMode::BMC_None);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-03 14:20:54
	 * 
	 * 匹配字符串并跳过
	 * 
	 * @param matchStr 匹配字符串
	 * @param notMatchStr 不匹配字符串
	 * @param mode 匹配模式
	 * 
	 * @note 使用 KMP 算法进行字符串匹配
	 */
	void skipUntil(const char * matchStr, const char * notMatchStr, uint32_t mode = BufferMatchMode::BMC_None);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 14:27:27
	 * 
	 * 读取对象
	 * @return 返回对象指针
	 * 
	 * @attention 读取失败时会抛出 [VLBufferException] 异常
	 * 
	 * @note 内存拷贝生成对象, 对象必须存在空构造函数
	 */
	template<typename T>
	std::shared_ptr<T> readObject() {
		uint32_t objectSize = sizeof(T);
		if(this->readIdx + objectSize > this->bufferLength) {
			throw VLBufferException("read object failed: out of range");
		}

		T* tPtr = new T;
		if(tPtr == nullptr) {
			throw VLBufferException("read object failed: create instance of class failed");
		}

		::memcpy((void *)tPtr, this->buffer + this->readIdx, objectSize);

		this->readIdx += objectSize;

		return std::shared_ptr<T>(tPtr);
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 18:59:41
	 * 
	 * 写入字节
	 * 
	 * @param byte 字节
	 */
	void writeByte(const uint8_t& byte);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 19:00:48
	 * 
	 * 写入整数
	 * 
	 * @param number 整数
	 * @param byteLength 表示 N 字节整数, 默认为 4
	 * @param bigEndian 是否为大端字节序写入
	 */
	void writeInt(long long number, unsigned char byteLength = 4, bool bigEndian = true);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 19:07:54
	 * 
	 * 写入浮点数
	 * 
	 * @param number 浮点数
	 * @param bigEndian 是否为大端字节序写入
	 */
	void writeFloat(float number, bool bigEndian = true);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-19 19:07:54
	 * 
	 * 写入双精度浮点数
	 * 
	 * @param number 双精度浮点数
	 * @param bigEndian 是否为大端字节序写入
	 */
	void writeDouble(double number, bool bigEndian = true);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 14:34:47
	 * 
	 * 写入对象
	 * 
	 * @param objPtr 对象指针
	 */
	template<typename T>
	void writeObject(T* objPtr) {
		uint32_t objectSize = sizeof(T);
		this->appendBuffer((uint8_t *)objPtr, 0, objectSize);
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-02-19 11:09:52
	 * 
	 * 向前方回滚 N 个字节
	 * @param count 回滚字节数
	 * @return 回滚成功返回 true
	 */
	bool forward(int32_t count = 1);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-02-19 11:10:44
	 * 
	 * 向后方前进 N 个字节
	 * @param count 前进字节数
	 * @return 前进成功返回 true
	 */
	bool backward(int32_t count = 1);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-02-19 11:11:16
	 * 
	 * 将读取下标设置到指定位置
	 * @param position 指定位置
	 * @return 设置成功返回 true
	 */
	bool seek(int32_t position);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-02-21 15:32:07
	 * 
	 * 丢弃已经读过的字节缓冲
	 */
	void dropRead();
private:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 14:15:15
	 * 
	 * 内部缓冲指针
	 */
	uint8_t* buffer;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-02-15 17:17:33
	 * 
	 * 读取下标
	 */
	uint32_t readIdx;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 14:23:49
	 * 
	 * 已缓冲长度
	 */
	uint32_t bufferLength;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 14:24:10
	 * 
	 * 缓冲区长度
	 */
	uint32_t totalLength;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-01-07 14:33:06
	 * 
	 * 添加指定字节到当前缓冲区指定位置中
	 * @param currentOffset 当前字节缓冲区指定位置
	 * @param buffer 字节数组指针
	 * @param offset 偏移位置
	 * @param length 转移长度
	 */
	void _appendBuffer(uint32_t currentOffset, const uint8_t* buffer, uint32_t offset, uint32_t length);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-01 19:13:52
	 * 
	 * 匹配字符串, 直到发现相同串时返回字节缓冲区的起始下标和终止下标
	 * 
	 * @param matchStr 待匹配子串
	 * @param notMatchStr 不匹配子串
	 * @param mode 匹配模式
	 * 
	 * @note 不匹配子串长度要大于待匹配子串的长度, 否则无法生效
	 * @note 通过 [mode] 来设置匹配模式, 参见 [BufferMatchMode]
	 */
	BufferRange _matchString(const char * matchStr, const char * notMatchStr, uint32_t mode);
};


}


#endif //VL_BUFFER__H_
